package wetalk.software.bupt.com.wetalk.application;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.util.Log;

import com.jyt.message.Message;
import com.jyt.util.MySerializable;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.util.List;
import java.util.Map;

import cn.bmob.im.inteface.DownloadListener;
import cn.bmob.v3.listener.FindListener;
import wetalk.software.bupt.com.wetalk.DB.WeTalkDB;
import wetalk.software.bupt.com.wetalk.GloableParams;
import wetalk.software.bupt.com.wetalk.communicate.AndroidClientService;
import wetalk.software.bupt.com.wetalk.listener.OnReceiveListener;
import wetalk.software.bupt.com.wetalk.listener.PushListener;
import wetalk.software.bupt.com.wetalk.listener.UploadListener;
import wetalk.software.bupt.com.wetalk.model.po.ChatUser;
import wetalk.software.bupt.com.wetalk.model.po.WeTalkConstant;
import wetalk.software.bupt.com.wetalk.model.po.WeTalkMsg;
import wetalk.software.bupt.com.wetalk.model.po.WeTalkRecent;
import wetalk.software.bupt.com.wetalk.util.WeTalkConfig;
import wetalk.software.bupt.com.wetalk.util.WeTalkJsonUtil;
import wetalk.software.bupt.com.wetalk.util.WeTalkLog;
import wetalk.software.bupt.com.wetalk.util.WeTalkNetUtil;
import wetalk.software.bupt.com.wetalk.util.WeTalkUtils;

import static android.content.Context.BIND_AUTO_CREATE;

/**
 * Created by zhangjie on 2017/12/6.
 */

public class ChatManager {
    private static final String TAG = "ChatManager";

    private Context globalContext;
    // 创建private static类实例
    private volatile static ChatManager INSTANCE;
    // 同步锁
    private static Object INSTANCE_LOCK = new Object();

    private AndroidClientService.SendBinder sendBinder;

    private ServiceConnection connection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            sendBinder = (AndroidClientService.SendBinder) iBinder;
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
        }
    };


    /**
     * 使用单例模式创建--双重锁定
     */
    public static ChatManager getInstance(Context context) {
        if (INSTANCE == null)
            synchronized (INSTANCE_LOCK) {
                if (INSTANCE == null) {
                    INSTANCE = new ChatManager();
                }
                INSTANCE.init(context);
            }
        return INSTANCE;
    }

    private boolean isBound = false;

    /**
     * 初始化
     *
     * @param context
     * @return void
     * @throws
     * @Title: init
     * @Description: TODO
     */
    public void init(Context context) {
        this.globalContext = context;
        bindService();
    }

    public void bindService() {
        if (sendBinder == null) {
            Intent bindIntent = new Intent(globalContext, AndroidClientService.class);
            isBound = globalContext.bindService(bindIntent, connection, BIND_AUTO_CREATE);
        }
    }

    public void unBindService() {
        if (isBound) {
            globalContext.unbindService(connection);
            isBound = false;
        }
    }

    /**
     * 允许开发者自定义提示语
     *
     * @param msg
     * @param showName
     * @return
     */
    private JSONObject createSendMessage(final WeTalkMsg msg, String showName) {
        try {
            JSONObject json = new JSONObject();
            String conversationId = msg.getConversationId();
            String toId = conversationId.split("&")[1];
            json.put(WeTalkConstant.PUSH_KEY_TOID, toId);
            json.put(WeTalkConstant.PUSH_KEY_TARGETID, msg.getBelongId());//fromID
            // 发送的内容
            json.put(WeTalkConstant.PUSH_KEY_CONTENT, msg.getContent());
            // 增加消息时间
            json.put(WeTalkConstant.PUSH_KEY_MSGTIME, msg.getMsgTime());
            // 额外字段
            //TODO:find targetID
            //json.put("targetID", msg.getBelongId());
            return json;
        } catch (JSONException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return null;
        }
    }

    private JSONObject createSendFileMessage(final WeTalkMsg msg, String local) {
        try {
            JSONObject json = new JSONObject();
            String content = local;

            String conversationId = msg.getConversationId();
            String toId = conversationId.split("&")[1];
            json.put(WeTalkConstant.PUSH_KEY_TOID, toId);
            json.put(WeTalkConstant.PUSH_KEY_TARGETID, msg.getBelongId());//fromID
            // 发送的内容
            json.put(WeTalkConstant.PUSH_KEY_CONTENT, content);
            // 增加消息时间
            json.put(WeTalkConstant.PUSH_KEY_MSGTIME, msg.getMsgTime());
            return json;
        } catch (JSONException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 给指定objectId的用户发送文本消息，提供推送回调操作
     *
     * @param targetUser:当前的聊天用户
     * @param msg                消息实体
     * @return void
     * @throws
     */
    private void sendTextMessage(final ChatUser targetUser, final WeTalkMsg msg, final PushListener pushcallback) {
        boolean isNetConnected = WeTalkNetUtil.isNetworkAvailable(globalContext);
        if (!isNetConnected) {// 若无网络
            uploadAndSaveMsg(false, targetUser, msg);
            pushcallback.onFailure(WeTalkConfig.CODE_NETWORK_ERROR, "网络连接失败，请检查网络!");
            return;
        }
        baseSendMessage(false, targetUser, msg, null, pushcallback);
    }

    /**
     * 给指定用户发送文本类型的消息：包括文字加表情和位置消息 默认推送成功之后存储消息到数据库（本地和WeTalk）
     *
     * @return
     * @throws
     * @Title: sendMessage
     * @Description: TODO
     * tent
     */
    public void sendTextMessage(final ChatUser targetUser, final WeTalkMsg msg) {
        sendTextMessage(targetUser, msg, new PushListener() {
            @Override
            public void onSuccess() {
                WeTalkLog.i("sendTextMessage---> pushMessage:发送成功");
            }

            @Override
            public void onFailure(int arg0, String arg1) {
                WeTalkLog.i("sendTextMessage---> 发送失败：code = " + arg0 + ",错误描述 = " + arg1);
            }
        });
    }

    private void baseSendMessage(final boolean isResend, final ChatUser targetUser, final WeTalkMsg msg, final String showAlert, final PushListener pushcallback) {
        //直接发送消息--用子线程+service发送消息
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    send(targetUser, createSendMessage(msg, showAlert), new PushListener() {
                        @Override
                        public void onSuccess() {
                            WeTalkLog.i("baseSendMessage---> pushMessage:发送成功");
                            msg.setStatus(WeTalkConfig.STATUS_SEND_SUCCESS);
                            uploadAndSaveMsg(true, targetUser, msg);
                            pushcallback.onSuccess();
                        }

                        @Override
                        public void onFailure(int arg0, String arg1) {
                            WeTalkLog.i("baseSendMessage---> pushMessage:发送失败" + arg1);
                            if (!isResend) {
                                uploadAndSaveMsg(false, targetUser, msg);
                            }
                            pushcallback.onFailure(arg0, arg1);
                        }
                    });
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    public void sendGroupTextMessage(final WeTalkMsg msg, final ChatUser targetUser) {
        final Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    sendGroupMsg(createSendMessage(msg, ""), new PushListener() {
                        @Override
                        public void onSuccess() {
                            WeTalkLog.i("群消息---> pushMessage:发送成功");
                            msg.setStatus(WeTalkConfig.STATUS_SEND_SUCCESS);
                            uploadAndSaveMsg(true, targetUser, msg);
                        }

                        @Override
                        public void onFailure(int arg0, String arg1) {
                            WeTalkLog.i("群消息---> pushMessage:发送失败" + arg1);
                        }
                    });
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
    }

    /**
     * 重发文本消息
     *
     * @param targetUser
     * @param msg
     * @return
     * @throws
     * @Title: resendMessage
     * @Description: TODO
     */
    public void resendTextMessage(final ChatUser targetUser, final WeTalkMsg msg, final PushListener pushcallback) {
        boolean isNetConnected = WeTalkNetUtil.isNetworkAvailable(globalContext);
        if (!isNetConnected) {// 若无网络
            uploadAndSaveMsg(false, targetUser, msg);
            pushcallback.onFailure(WeTalkConfig.CODE_NETWORK_ERROR, "网络连接失败，请检查网络!");
            return;
        }
        baseSendMessage(true, targetUser, msg, null, pushcallback);
    }

    public void resendFileMessage(final ChatUser targetUser, final WeTalkMsg msg, final UploadListener uploadCallback) {
        uploadCallback.onStart(msg);
        boolean isNetConnected = WeTalkNetUtil.isNetworkAvailable(globalContext);
        if (!isNetConnected) {// 若无网络
            msg.setStatus(WeTalkConfig.STATUS_SEND_FAIL);
            saveSendMessage(targetUser, msg);
            uploadCallback.onFailure(WeTalkConfig.CODE_NETWORK_ERROR, "网络连接失败，请检查网络!");
        } else {
            String content = msg.getContent();
            String local = "";
            if (msg.getMsgType() == WeTalkConfig.TYPE_VOICE) {// 语音格式的字符串的组装格式:语音文件的本地地址&长度
                local = content.split("&")[0];
            } else if (msg.getMsgType() == WeTalkConfig.TYPE_IMAGE) {// 图片格式的字符串的组装格式:file:///图片文件的本地地址
                local = content.substring(8);
            }
            // 而后上传该图片
            uploadAction(local, msg, targetUser, "msg_pic", uploadCallback);
        }
    }

    public void sendImageMessage(final ChatUser targetUser, final String localPath, final UploadListener uploadCallback) {
        sendExtraMessage(targetUser, localPath, uploadCallback, WeTalkConfig.TYPE_IMAGE, 0);
    }

    public void sendFileMessage(final ChatUser targetUser, final String localPath, final UploadListener uploadCallback) {
        sendExtraMessage(targetUser, localPath, uploadCallback, WeTalkConfig.TYPE_FILE, 0);
    }

    public void sendVoiceMessage(final ChatUser targetUser, final String localPath, int length, final UploadListener uploadCallback) {
        sendExtraMessage(targetUser, localPath, uploadCallback, WeTalkConfig.TYPE_VOICE, length);
    }

    private void sendExtraMessage(ChatUser targetUser, String localPath, UploadListener uploadCallback, int type, int length) {
        // 先构建一个可用于界面显示的msg对象：
        // 图片的话，刚开始存储的是本地地址（前面加file:///，用于显示本地图片），下载完成之后存储的是本地地址+"&"+网络后的地址,传送给对方的还是网络地址
        // 文件可能不需要加
        String content = "", sendType = "";
        switch (type) {
            case WeTalkConfig.TYPE_VOICE:
                content = localPath + "&" + length;
                sendType = "msg_voice";
                break;
            case WeTalkConfig.TYPE_IMAGE:
                content = "file:///" + localPath;
                sendType = "msg_pic";
                break;
            case WeTalkConfig.TYPE_FILE:
                content = "file:///" + localPath;
                sendType = "msg_file";
                break;
        }

        final WeTalkMsg msg = WeTalkMsg.createSendMessage(globalContext, String.valueOf(targetUser.getUserID()),
                content, WeTalkConfig.STATUS_SEND_START, type);

        // 若无网络则存储的是本地的地址,暂时先不做上传
        boolean isNetConnected = WeTalkNetUtil.isNetworkAvailable(globalContext);
        if (!isNetConnected) {// 若无网络
            uploadAndSaveMsg(false, targetUser, msg);
            uploadCallback.onFailure(WeTalkConfig.CODE_NETWORK_ERROR, "网络连接失败，请检查网络！");
        } else {
            //上传文件到服务器，服务器收到一个url，把这个url发给另一个用户
            uploadAction(localPath, msg, targetUser, sendType, uploadCallback);
        }
    }

    private void uploadAction(final String local, final WeTalkMsg msg, final ChatUser targetUser, String type, final UploadListener uploadCallback) {
        String[] tmp = local.split("/");
        String fileName = tmp[tmp.length - 1];
        try {
            long fileSize=WeTalkUtils.getFileSize(new File(local));
            if(fileSize>1000000){
                //1MB
                uploadCallback.onFailure(1,"File Too Large");
                return;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        //给服务端发送url
        JSONObject file = createSendFileMessage(msg, GloableParams.fileUrl + fileName);
        byte[] bs;
        bs = MySerializable.object_bytes(file.toString());
        String userId = msg.getBelongId();
        String targetId = targetUser.getUserID();
        Log.i(TAG, "uploadAction: file" + userId + " to " + targetId);
        final Message finalMsg = new Message(userId, targetId, type, bs);

        UploadManager uploadManager = new UploadManager(globalContext, msg, new DownloadListener() {
            @Override
            public void onStart() {
                uploadCallback.onStart(msg);
            }

            @Override
            public void onSuccess() {
                uploadCallback.onSuccess();
                uploadAndSaveMsg(true, targetUser, msg);
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        sendBinder.sendMessage(finalMsg);
                    }
                }).start();
            }

            @Override
            public void onError(String s) {
                uploadAndSaveMsg(false, targetUser, msg);
                uploadCallback.onFailure(1, "error");
            }
        });

        uploadManager.execute(GloableParams.uploadUrl, local);
    }

    /**
     * 上传服务器并保存到本地 uploadAndSaveMsg
     *
     * @param isSuccess
     * @param targetUser
     * @param msg
     * @return
     * @throws
     * @Title: uploadAndSaveMsg
     * @Description: TODO
     */
    private void uploadAndSaveMsg(boolean isSuccess, ChatUser targetUser, WeTalkMsg msg) {
        if (!isSuccess) {// 不成功
            msg.setStatus(WeTalkConfig.STATUS_SEND_FAIL);
        } else {
            msg.setStatus(WeTalkConfig.STATUS_SEND_SUCCESS);
            // 保存消息到服务器
            //insertMessage(msg);
        }
        // 保存消息到本地数据库
        saveSendMessage(targetUser, msg);
    }

    /**
     * 发送消息时候,保存消息到本地数据库
     *
     * @param targetUser
     * @param msg
     * @return
     * @throws
     * @Title: saveSendMessage
     * @Description: TODO
     */
    private void saveSendMessage(ChatUser targetUser, WeTalkMsg msg) {
        //发送的消息其都是已读状态的
        msg.setIsReaded(WeTalkConfig.STATE_READED);
        //保存到本地消息表中
        WeTalkDB.create(globalContext).saveMessage(msg);
        // 保存到最近会话列表
        WeTalkRecent recent = new WeTalkRecent(String.valueOf(targetUser.getUserID()),
                targetUser.getUserName(), targetUser.getNick(),
                targetUser.getAvatar(), msg.getContent(),
                Long.parseLong(msg.getMsgTime()), msg.getMsgType());
        WeTalkDB.create(globalContext).saveRecent(recent);
    }

    /**
     * 获取指定会话id和时间的消息
     *
     * @param conversionId
     * @param msgTime
     * @return WeTalkMsg
     * @throws
     * @Title: getMessage
     * @Description: TODO
     */
    public WeTalkMsg getMessage(final String conversionId, final String msgTime) {
        return WeTalkDB.create(globalContext).getMessage(conversionId, msgTime);
    }

    /**
     * 保存收到的消息到本地,并根据isAskReaded来判断是否发送已读回执
     *
     * @param isAskReaded：是否发送消息已读回执
     * @param msg
     * @return
     * @throws
     * @Description: TODO
     */
    public void saveReceiveMessage(boolean isAskReaded, WeTalkMsg msg) {
        String convertId = msg.getConversationId();
        String toId = convertId.split("&")[1];// 这条消息是发送给谁的
        String loginid = UserManager.getInstance(globalContext).getCurrentUserObjectId();
        //收到的消息全部是未读已收到状态
        msg.setIsReaded(WeTalkConfig.STATE_UNREAD_RECEIVED);
        WeTalkDB.create(globalContext, toId).saveMessage(msg);
        // 保存到最近会话列表
        WeTalkRecent recent = new WeTalkRecent(msg.getBelongId(),
                msg.getBelongUsername(), msg.getBelongNick(),
                msg.getBelongAvatar(), msg.getContent(),
                Long.parseLong(msg.getMsgTime()), msg.getMsgType());
        WeTalkDB.create(globalContext, toId).saveRecent(recent);
    }

    /**
     * 保存从推送消息中取到的好友请求，更新后台的未读标示
     *
     * @param json
     * @param toId
     * @return WeTalkInvitation
     * @throws
     * @Title: saveReceiveInvite
     * @Description: TODO
     */
    public WeTalkInvitation saveReceiveInvite(String json, String toId) {
        WeTalkInvitation message = WeTalkInvitation.createReceiverInvitation(json);
        //保存好友请求消息
        //WeTalkDB.create(globalContext,toId).saveInviteMessage(message);
        //已读消息发送成功之后更新该条消息的读取状态为已读
        return message;
    }


    private void send(ChatUser targetUser, JSONObject json, PushListener pushCallback) throws JSONException {
        //需要自己的id
        String targetId = targetUser.getUserID();
        String userId = UserManager.getInstance(globalContext).getCurrentUser().getUserID();
        Log.i(TAG, "send-json: " + json);
        //用自己的服务器,使用service发消息

        byte[] bs;
        bs = MySerializable.object_bytes(json.toString());
        Message msg = new Message(userId, targetId, "msg", bs);
        if (sendBinder != null) {
            sendBinder.sendMessage(msg);
            pushCallback.onSuccess();
        } else {
            pushCallback.onFailure(1, "no binder");
        }

    }

    //发送群消息函数
    private void sendGroupMsg(final JSONObject json, PushListener pushCallback) throws JSONException {
        Log.i(TAG, "send-json: " + json);
        String userId = UserManager.getInstance(globalContext).getCurrentUser().getUserID();
        byte[] bs;
        bs = MySerializable.object_bytes(json.toString());
        Message groupMsg = new Message(userId, "sys_group", "groupMsg", bs);
        sendBinder.sendMessage(groupMsg);
        pushCallback.onSuccess();
    }

    /**
     * 创建收到的消息
     *
     * @param
     * @return
     * @throws
     * @Description: TODO
     */
    public void createReceiveMsg(String json, final String msgType, final OnReceiveListener receiveCallBack) {
        JSONObject jo;
        try {
            jo = new JSONObject(json);
            final String tag = WeTalkJsonUtil.getString(jo, WeTalkConstant.PUSH_KEY_TAG);
            //增加消息接收方的ObjectId--目的是解决多账户登陆同一设备时，无法接到到非当前登陆用户的消息。
            final String toId = WeTalkJsonUtil.getString(jo, WeTalkConstant.PUSH_KEY_TOID);
            final String fromId = WeTalkJsonUtil.getString(jo, WeTalkConstant.PUSH_KEY_TARGETID);
            final String msgTime = WeTalkJsonUtil.getString(jo, WeTalkConstant.PUSH_KEY_MSGTIME);
            final String content = WeTalkJsonUtil.getString(jo, WeTalkConstant.PUSH_KEY_CONTENT);

            //消息发送方的信息
            //1、如果是你的好友，从好友数据库中获取该用户的资料 2、如果是群资料呢
            Map<String, ChatUser> map;

            if (fromId.contains("_g")) {
                map = WeTalkUtils.list2map(WeTalkDB.create(globalContext, toId).getGroupList());
            } else {
                map = WeTalkUtils.list2map(WeTalkDB.create(globalContext, toId).getContactList());
            }

            ChatUser targetUser = map.get(fromId);
            if (targetUser != null) {
                checkWeTalkMsg(targetUser, tag, fromId, toId, content, msgTime, msgType, receiveCallBack);
            } else {//陌生人
                WeTalkLog.i("ObjectId为" + fromId + "的用户不是您的好友");
                UserManager.getInstance(globalContext).queryUserById(fromId, new FindListener<ChatUser>() {
                    @Override
                    public void onError(int arg0, String arg1) {
                        receiveCallBack.onFailure(arg0, arg1);
                    }

                    @Override
                    public void onSuccess(List<ChatUser> arg0) {
                        if (arg0 != null && arg0.size() > 0) {
                            ChatUser user = arg0.get(0);
                            checkWeTalkMsg(user, tag, fromId, toId, content, msgTime, msgType, receiveCallBack);
                        } else {
                            receiveCallBack.onFailure(WeTalkConfig.CODE_COMMON_FAILURE, "未查询到发送方的信息!");
                        }
                    }
                });
            }
        } catch (Exception e) {
            WeTalkLog.i("parseMessage错误：" + e.getMessage());
        }
    }

    private void checkWeTalkMsg(ChatUser user, String tag, String fromId, String toId, String content, String msgTime, String msgtype, OnReceiveListener receiveCallBack) {
        String username = user.getUserName();
        String avatar = user.getAvatar();
        String nick = user.getNick();
        Integer intMsgType = 0;
        switch (msgtype) {
            case "msg":
                intMsgType = WeTalkConfig.TYPE_TEXT;
                break;
            case "msg_voice":
                intMsgType = WeTalkConfig.TYPE_VOICE;
                break;
            case "msg_file":
                intMsgType = WeTalkConfig.TYPE_FILE;
                break;
            case "msg_pic":
                intMsgType = WeTalkConfig.TYPE_IMAGE;
                break;
        }
        WeTalkMsg msg = new WeTalkMsg(tag, fromId + "&" + toId, content, toId, fromId, username == null ? "" : username, avatar == null ? "" : avatar, nick == null ? "" : nick,
                msgTime, intMsgType, WeTalkConfig.STATE_UNREAD_RECEIVED, WeTalkConfig.STATUS_SEND_RECEIVERED);
        ChatUser currentUser = UserManager.getInstance(globalContext).getCurrentUser();
        if (currentUser != null) {//当前设备没有账号在登陆
            if (msg.getToId().equals(currentUser.getUserID())) {//只有当toId和当前登陆用户相符合才继续向下传递和弹通知，防止别人恶意刷消息
                boolean isExist = WeTalkDB.create(globalContext).checkTargetMsgExist(msg.getConversationId(), msg.getMsgTime());
                if (!isExist) {
                    //这里要判断下，取到的聊天消息是否存储过，因为存在这种情况，当推送链接没连上的时候，全部采用的是定点任务取到的，
                    //而当推送又连接到时候，那些离线消息本机就会接收到，这时候就应该考虑下
                    saveReceiveMessage(true, msg);
                    receiveCallBack.onSuccess(msg);
                } else {
                    receiveCallBack.onFailure(WeTalkConfig.CODE_COMMON_EXISTED, "该消息记录本地已存在");
                }
            } else {//当前登陆账号不是toId的情况下，也应该存储收到的消息。
                saveReceiveMessage(true, msg);
            }
        } else {//当之前的用户B注销登陆了，但又没有登陆其他设备，那么A再向B发的消息就应该存储到对应B账户的数据库中
            saveReceiveMessage(true, msg);
        }
    }

}
